# coding=utf-8

from typing import Dict, List
from utils.payload import ArrayPayload,TypeEnum, BaseTypePayload, EncodeType, Payload, StructMember, StructPayload

class CB:

    def __init__(self, opId: str, api: str, data: str, desc: str) -> None:
        self.op_id = opId
        self.api = api
        self.desc = desc
        # 检查数组
        self.__has_array = data.find("[") != -1 and data.endswith("]")
        if self.__has_array:
            self.array_len = int(data[data.find("[")+1:len(data)-1])
            self.data = data[:data.find("[")]
        else:
            self.array_len = 0
            self.data = data
        self.__payload: Payload = None

    def parsePayload(self, encodeTypeDict: Dict[str, EncodeType], errorFn):
        payload = Payload()
        if self.data.startswith("struct_"):
            name = self.data[len("struct_"):]
            if self.__has_array:
                errorFn("当前不支持发送结构提数组定义,该问题定义在{} 类型{}".format(
                    self.api, self.data))
            encodeType = encodeTypeDict.get(name, None)
            if encodeType is None:
                errorFn("接口{} 存在未知自定义类型{}, 请联系接口提供者添加完善".format(
                    self.api, self.data))
            structPayload = StructPayload(
                encodeType.getTypeName(), "value")
            for (member, desc) in encodeType.members:
                (type_name, name) = member.split(" ")
                structPayload.addMember(StructMember(
                    TypeEnum.toEnum(type_name), name, desc))
            payload.setStructPayload(structPayload)
        else:
            # 对于CB来说不是自定义结构提那么就是单一值或者数组
            data_type = self.data
            dataEnumEncoding = None

            if data_type == "string":
                if not self.__has_array:
                    errorFn("接口{} 存在string类型成员但没有定义长度".format(self.api))
                payload.setArrayPayload(ArrayPayload(
                        TypeEnum.string, "value", self.array_len))
            elif data_type.startswith("enum_"):
                data_type = data_type[len("enum_"):]
                dataEnumEncoding = encodeTypeDict.get(data_type, None)
                if dataEnumEncoding is None:
                    errorFn("接口{} 存在未知自定义类型{}, 请联系接口提供者添加完善".format(
                        self.api, self.data))
                payload.setBaseTypePayload(BaseTypePayload(
                        TypeEnum.int32_t, "value", dataEnumEncoding))
            else:
                if self.__has_array:
                        payload.setArrayPayload(ArrayPayload(
                            TypeEnum.toEnum(data_type), "value", self.array_len))
                else:
                    cb_type=TypeEnum.toEnum(data_type)
                    if cb_type is None:
                        dataEnumEncoding = encodeTypeDict.get(data_type, None)
                        if dataEnumEncoding is None:
                            errorFn("接口{} 存在未知自定义类型{}, 请联系接口提供者添加完善".format(
                                self.api, self.data))
                        payload.setBaseTypePayload(BaseTypePayload(
                            TypeEnum.int32_t, "value", dataEnumEncoding))
                    else:
                        payload.setBaseTypePayload(BaseTypePayload(
                            cb_type, "value"))

        self.__payload = payload

    def get_send_struct_name(self) -> str:
        return "Op{0}_SetRequestNoReturn".format(self.api.replace("_",""))

    def get_op_id_define(self) -> str:
        return "{0}OpID ({1})".format(self.api.replace("_","").lower(), hex(int(self.op_id)))

    def getPayload(self) -> Payload:
        return self.__payload

    def getSetMethodParamType(self) -> str:
        payload = self.getPayload()
        if payload.isBaseTypePayload():
            basePayload = payload.getBaseTypePayload()
            if basePayload.hasEncodeType():
                return "sdb::"+basePayload.getEncodeType().getTypeName()
            else:
                return basePayload.type.name
        elif payload.isArrayPayload():
            arrayPayload = payload.getArrayPayload()
            if self.data == "string":
                return "const std::string&"
            else:
                return "const std::array<{},{}>&".format(arrayPayload.array_type.name,arrayPayload.array_len)
        else:
            structPayload = payload.getStructPayload()
            return "const {}&".format(structPayload.type_name)

    def getTypesHalType(self) -> str:
        payload = self.getPayload()
        if payload.isStructPayload():
            return 'BYTES'
        elif payload.isArrayPayload():
            payload_type = payload.getArrayPayload().array_type
            if payload_type == TypeEnum.string:
                return 'STRING'
            else:
                return 'INT32_VEC'
        else:
            payload_type = payload.getBaseTypePayload().type
            if payload_type == TypeEnum.string:
                return 'STRING'
            else:
                return 'INT32'
    
    def toSignalObjMember(self):
        payload = self.getPayload()
        if payload.isBaseTypePayload():
            if payload.getBaseTypePayload().type == TypeEnum.string:
                return "stringValues"
            elif payload.getBaseTypePayload().type == TypeEnum.float:
                return "floatValues"
            elif payload.getBaseTypePayload().type == TypeEnum.int64_t or payload.getBaseTypePayload().type == TypeEnum.uint64_t:
                return "int64Values"
            return "int32Values"
        elif payload.isArrayPayload():
            payload_type=payload.getArrayPayload().array_type
            if payload_type == TypeEnum.string:
                return "string"
            else:
                return "int32Values"
        else:
            return "bytes"
    
    def hasEnum(self)->bool:
        payload = self.getPayload()
        if payload.isBaseTypePayload():
            return payload.getBaseTypePayload().hasEncodeType()
        else:
            return False



class PA:
    def __init__(self, opId: str, api: str, available: str, data: str, status: str, format: str, desc: str) -> None:
        self.op_id = opId
        self.api = api
        self.desc = desc
        self.available = available
        self.status = status
        self.format = format
        # 检查data部分是否有数组
        self.__has_array = data.find("[") != -1 and data.endswith("]")
        if self.__has_array:
            self.array_len = int(data[data.find("[")+1:len(data)-1])
            self.data = data[:data.find("[")]
        else:
            self.array_len = 0
            self.data = data

        self.__payload: Payload = None

    def mustGenProto(self)->bool:
        return self.data.startswith("struct_") or self.status != "NA" or self.status != "NA"

    def toProtoBufName(self)->str:
        if self.data.startswith("struct_"):
            return self.getPayload().getStructPayload().type_name
        else:
            return self.api

    def parsePayload(self, encodeTypeDict: Dict[str, EncodeType], errorFn):
        payload = Payload()
        if self.data.startswith("struct_"):
            name = self.data[len("struct_"):]
            if self.__has_array:
                errorFn("当前不支持接收结构提数组定义,该问题定义在{} 类型{}".format(
                    self.api, self.data))
            encodeType = encodeTypeDict.get(name, None)
            if encodeType is None:
                errorFn("接口{} 存在未知自定义类型{}, 请联系接口提供者添加完善".format(
                    self.api, self.data))
            
            structPayload = StructPayload(
                encodeType.getTypeName(), self.api.replace("_","").lower())
            for (member, desc) in encodeType.members:
                (type_name, name) = member.split(" ")
                structPayload.addMember(StructMember(
                    TypeEnum.toEnum(type_name), name, desc))
            payload.setStructPayload(structPayload)
        elif self.data == "string":
            if not self.__has_array:
                errorFn("接口{} 存在string类型成员但没有定义长度".format(self.api))
            
            structName = "PAStringFuncType{}".format(self.array_len)
            structPayload = StructPayload(structName, self.api.replace("_","").lower())
            availableEnumEncoding = None
            dataEnumEncoding = None
            formatEnumEncoding = None
            statusEnumEncoding = None
            if self.available.upper() != "NA":
                if self.available.startswith("enum_"):
                    self.available = self.available[len("enum_"):]
                    availableEnumEncoding = encodeTypeDict.get(
                        self.available, None)
            if self.format.upper() != "NA":
                if self.format.startswith("enum_"):
                    self.format = self.format[len("enum_"):]
                    formatEnumEncoding = encodeTypeDict.get(self.format, None)
            if self.status.upper() != "NA":
                if self.status.startswith("enum_"):
                    self.status = self.status[len("enum_"):]
                    statusEnumEncoding = encodeTypeDict.get(self.status, None)
            structPayload.addMember(StructMember(
                TypeEnum.int32_t, "availability", encodeType=availableEnumEncoding))
            structPayload.addMember(StructMember(
                TypeEnum.uint8_t, "data[{}]".format(str(self.array_len))))
            structPayload.addMember(StructMember(
                TypeEnum.int32_t, "format", encodeType=formatEnumEncoding))
            structPayload.addMember(StructMember(
                TypeEnum.int32_t, "status", encodeType=statusEnumEncoding))
            payload.setStructPayload(structPayload)
        else:
            # 对于pa来说只有就是通用PA成员
            availableEnumEncoding = None
            dataEnumEncoding = None
            formatEnumEncoding = None
            statusEnumEncoding = None
            if self.available.upper() != "NA":
                if self.available.startswith("enum_"):
                    self.available = self.available[len("enum_"):]
                    availableEnumEncoding = encodeTypeDict.get(
                        self.available, None)
            
            if self.data.startswith("enum_"):
                self.data = self.data[len("enum_"):]
                dataEnumEncoding = encodeTypeDict.get(self.data, None)
            
            if self.format.upper() != "NA":
                if self.format.startswith("enum_"):
                    self.format = self.format[len("enum_"):]
                    formatEnumEncoding = encodeTypeDict.get(self.format, None)

            if self.status.upper() != "NA":
                if self.status.startswith("enum_"):
                    self.status = self.status[len("enum_"):]
                    statusEnumEncoding = encodeTypeDict.get(self.status, None)
            
            structName = "PAIntFuncType"
            if self.__has_array:
                structName = "PAIntArrayFuncType{}".format(self.array_len)
            structPayload = StructPayload(structName, self.api.replace("_","").lower())
            structPayload.addMember(StructMember(
                TypeEnum.int32_t, "availability", encodeType=availableEnumEncoding))
            
            if self.data.upper() != "NA":
                if self.__has_array:
                    structPayload.addMember(StructMember(
                        TypeEnum.uint8_t, "data[{}]".format(str(self.array_len))))
                else:
                    structPayload.addMember(StructMember(TypeEnum.int32_t, "data",encodeType=dataEnumEncoding))

            structPayload.addMember(StructMember(
                TypeEnum.int32_t, "format", encodeType=formatEnumEncoding))
            structPayload.addMember(StructMember(
                TypeEnum.int32_t, "status", encodeType=statusEnumEncoding))
            payload.setStructPayload(structPayload)
        self.__payload = payload

    def getPayload(self) -> Payload:
        return self.__payload

    def getTypesHalType(self) -> str:
        if self.data.startswith('struct_') or (self.format != 'NA' or self.status != 'NA'):
            return 'BYTES'
        elif self.data == 'string':
            return 'STRING'
        elif self.__has_array:
            return 'INT32_VEC'
        else:
            return 'INT32'



class Module:

    def __init__(self, name: str, service_id: str) -> None:
        self.name = name
        self.service_id = service_id
        self.cb_apis: List[CB] = []
        self.pa_apis: List[PA] = []

    def addCB(self, cb: CB):
        self.cb_apis.append(cb)

    def addPA(self, pa: PA):
        self.pa_apis.append(pa)

    def get_notify_op_define(self) -> str:
        return "{0}NotificationOpID ({1})".format(self.name, hex(200))

    def get_notify_struct_name(self) -> str:
        return "_Op{0}_Notification".format(self.name)

